package org.hk.proxy.server.container;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import org.hk.proxy.common.container.Container;
import org.hk.proxy.common.container.ContainerHelper;
import org.hk.proxy.protocol.HeartBeatHandler;
import org.hk.proxy.protocol.ProxyMessageDecoder;
import org.hk.proxy.protocol.ProxyMessageEncoder;
import org.hk.proxy.server.channel.ProxyChannelManager;
import org.hk.proxy.server.constant.ProjectConfig;
import org.hk.proxy.server.handlers.ServerChannelHandler;
import org.hk.proxy.server.handlers.UserChannelHandler;
import org.hk.proxy.server.utils.CommonUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.BindException;
import java.net.InetSocketAddress;

public class ProxyServerContainer extends ContainerHelper implements Container {
    public static final Logger log = LoggerFactory.getLogger(ProxyServerContainer.class);

    /**
     * max packet is 2M.
     */
    private static final int MAX_FRAME_LENGTH = 2 * 1024 * 1024;

    private static final int LENGTH_FIELD_OFFSET = 0;

    private static final int LENGTH_FIELD_LENGTH = 4;

    private static final int INITIAL_BYTES_TO_STRIP = 0;

    private static final int LENGTH_ADJUSTMENT = 0;

    private NioEventLoopGroup serverWorkerGroup;

    private NioEventLoopGroup serverBossGroup;
    private ServerBootstrap userBootStrap;
    public ProxyServerContainer() {
        serverBossGroup = new NioEventLoopGroup(4);
        serverWorkerGroup = new NioEventLoopGroup(8);
    }
    @Override
    public void start() {
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        serverBootstrap.group(serverBossGroup,serverWorkerGroup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel ch){
                ch.pipeline().addLast(new ProxyMessageDecoder(MAX_FRAME_LENGTH, LENGTH_FIELD_OFFSET, LENGTH_FIELD_LENGTH, LENGTH_ADJUSTMENT, INITIAL_BYTES_TO_STRIP));
                ch.pipeline().addLast(new ProxyMessageEncoder());
                ch.pipeline().addLast(new ServerChannelHandler());
            }
        });
        serverBootstrap.bind(new InetSocketAddress(ProjectConfig.config.getPort()));
        startUserContainer();
    }
    private void startUserContainer(){
        userBootStrap = new ServerBootstrap();
        userBootStrap.group(serverBossGroup, serverWorkerGroup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {
            @Override
            public void initChannel(SocketChannel ch) {
                ch.pipeline().addLast(new UserChannelHandler());
            }
        });
        for (int port : ProxyChannelManager.returnBindPorts()) {
            try {
                userBootStrap.bind(port).get();
            } catch (Exception ex) {
                // BindException表示该端口已经绑定过
                if (!(ex.getCause() instanceof BindException)) {
                    log.error("监听端口异常:",ex);
                    throw new RuntimeException(ex);
                }
            }
        }
    }
    public void reFlush(){
        CommonUtils.initConfig();
        startUserContainer();
    }
    @Override
    public void stop() {
        serverBossGroup.shutdownGracefully();
        serverWorkerGroup.shutdownGracefully();
    }

}
